package net.esj.basic.core.engine.support.translator;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


import org.springframework.beans.BeanUtils;

public abstract class AbstractTranslator implements Translator {

	@Override
	public Object translate(Object source,
			Class targetClass) throws TranslateException {
		try{
			if(source instanceof Collection){
				return translateCollection((Collection) source, targetClass);
			}else if(source.getClass().isArray()){
				return translateArray((Object[]) source, targetClass);
			}else{
				Object o = targetClass.newInstance();
				doTrans(source, o);
				return o;
			}
		}catch(Throwable ex){
			throw new TranslateException("Could not copy properties from source to target", ex);
		}
	}

	@Override
	public  Object[] translateArray(Object[] objs,
			Class targetClass) throws TranslateException {
		Object[] ts = new Object[objs.length];
		try{
			for(int i=0;i<objs.length;i++){
				Object o = targetClass.newInstance();
				doTrans(objs[i], o);
				ts[i] = o;
			}
			return ts;
		}catch(Throwable ex){
			throw new TranslateException("Could not copy properties from source to target", ex);
		}
	}

	@Override
	public Collection translateCollection(
			Collection collections, Class targetClass)
			throws TranslateException {
		List list = new ArrayList();
		try{
				for(Object object : collections){
					Object o = targetClass.newInstance();
					doTrans(object,o);
					list.add(o);
				}
				return list;
		}catch(Throwable ex){
			throw new TranslateException("Could not copy properties from source to target", ex);
		}
	}

	
	@Override
	public Object retrans(Object source, Class targetClass)
			throws TranslateException {
		try{
			if(source instanceof Collection){
				return retransCollection((Collection) source, targetClass);
			}else if(source.getClass().isArray()){
				return retransArray((Object[]) source, targetClass);
			}else{
				Object o = targetClass.newInstance();
				doRetrans(source, o);
				return o;
			}
		}catch(Throwable ex){
			throw new TranslateException("Could not copy properties from source to target", ex);
		}
	}

	@Override
	public Object[] retransArray(Object[] objs, Class targetClass)
			throws TranslateException {
		Object[] ts = new Object[objs.length];
		try{
			for(int i=0;i<objs.length;i++){
				Object o = targetClass.newInstance();
				doRetrans(objs[i], o);
				ts[i] = o;
			}
			return ts;
		}catch(Throwable ex){
			throw new TranslateException("Could not copy properties from source to target", ex);
		}
	}

	@Override
	public Collection retransCollection(Collection collections,
			Class targetClass) throws TranslateException {
		List list = new ArrayList();
		try{
				for(Object object : collections){
					Object o = targetClass.newInstance();
					doRetrans(object,o);
					list.add(o);
				}
				return list;
		}catch(Throwable ex){
			throw new TranslateException("Could not copy properties from source to target", ex);
		}
	}
	
	protected void doTrans(Object source,Object target) throws TranslateException{
		Class<?> actualEditable = target.getClass();
		PropertyDescriptor[] targetPds =BeanUtils.getPropertyDescriptors(actualEditable);
		
		try{
			for (PropertyDescriptor targetPd : targetPds) {
				if (targetPd.getWriteMethod() != null ) {
					PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
					if (sourcePd != null && sourcePd.getReadMethod() != null) {
						Method readMethod = sourcePd.getReadMethod();
						if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
							readMethod.setAccessible(true);
						}
						Object value = readMethod.invoke(source);//获得原对象值
						
						if(value==null){
							continue;
						}
						
						Method writeMethod = targetPd.getWriteMethod();
						if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
							writeMethod.setAccessible(true);
						}
						
						value = handleTransBeforeWrite(value, targetPd, sourcePd);
						
						writeMethod.invoke(target, value);
					}
				}
			}
		}catch(IllegalAccessException e){
			throw new TranslateException(e);
		} catch (IllegalArgumentException e) {
			throw new TranslateException(e);
		} catch (InvocationTargetException e) {
			throw new TranslateException(e);
		}
	}

	protected void doRetrans(Object source,Object target) throws TranslateException{
		Class<?> actualEditable = target.getClass();
		PropertyDescriptor[] targetPds =BeanUtils.getPropertyDescriptors(actualEditable);
		
		try{
			for (PropertyDescriptor targetPd : targetPds) {
				if (targetPd.getWriteMethod() != null ) {
					PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
					if (sourcePd != null && sourcePd.getReadMethod() != null) {
						Method readMethod = sourcePd.getReadMethod();
						if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
							readMethod.setAccessible(true);
						}
						Object value = readMethod.invoke(source);//获得原对象值
						
						if(value==null){
							continue;
						}
						
						Method writeMethod = targetPd.getWriteMethod();
						if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
							writeMethod.setAccessible(true);
						}
						
						value = handleRetransBeforeWrite(value, targetPd, sourcePd);
						
						writeMethod.invoke(target, value);
					}
				}
			}
		}catch(IllegalAccessException e){
			throw new TranslateException(e);
		} catch (IllegalArgumentException e) {
			throw new TranslateException(e);
		} catch (InvocationTargetException e) {
			throw new TranslateException(e);
		}
	}
	
	/**
	 * 转换前处理
	 * @param value
	 * @param targetPd
	 * @param sourcePd
	 * @return
	 */
	protected abstract Object handleTransBeforeWrite(Object value,PropertyDescriptor targetPd,PropertyDescriptor sourcePd) throws TranslateException;
	
	/**
	 * 反译前处理
	 * @param value
	 * @param targetPd
	 * @param sourcePd
	 * @return
	 */
	protected abstract Object handleRetransBeforeWrite(Object value,PropertyDescriptor targetPd,PropertyDescriptor sourcePd) throws TranslateException;
}
